/*
 * FPEngine.java
 *
 * Copyright 2008 David D. Emory
 * 
 * This file is part of Five Points. See <http://www.fpdev.org> for
 * additional information regarding the project.
 * 
 * Five Points is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * Five Points is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with Five Points.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.fpdev.core;

import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.Envelope;
import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.geom.GeometryFactory;
import com.vividsolutions.jts.geom.LinearRing;
import com.vividsolutions.jts.geom.Polygon;
import com.vividsolutions.jts.geom.util.PolygonExtracter;
import org.fpdev.core.routenet.RouteNetwork;
import org.fpdev.core.algo.path.Pathfinder;
import java.sql.SQLException;
import org.fpdev.core.basenet.*;
import org.fpdev.core.transit.*;
import org.fpdev.core.data.LegacyDB;
import org.fpdev.core.transit.SubRouteInfo;
import org.fpdev.apps.rtemaster.GUIOutputAcceptor;

import java.io.*;
import java.util.Properties;
import java.awt.geom.*;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import org.fpdev.util.RectilinearPolygonDecomposer;


public class FPEngine {

  public final static String VERSION = "0.9.2"; // alpha 2
  private Properties sysProps_;
  private DataPackage dataPkg_;
  private BaseNetwork baseNet_;
  //private Scenarios scenarios_;
  private Routes routes_;
  private Stations stations_;
  private RouteNetwork rteNet_;
  private Locations locations_;
  private Landmarks landmarksNew_;
  private Pathfinder pathfinder_;
  private boolean initTE_;
  private Set<Integer> badElevLinkIDs_,  badAddrLinkIDs_,  badShpPtsLinkIDs_;
  private GUIOutputAcceptor guiOA_;

  public FPEngine(Properties sysProps) {
    
    System.out.println("Initializing FPEngine, version "+VERSION);

    sysProps_ = sysProps;
    baseNet_ = new BaseNetwork();
    guiOA_ = new DummyOutputAcceptor();

    badAddrLinkIDs_ = new HashSet<Integer>();
    badShpPtsLinkIDs_ = new HashSet<Integer>();
    badElevLinkIDs_ = new HashSet<Integer>();

    String dpName = getProperty("initDP");
    System.out.println("Loading initial data package " + dpName);
    dataPkg_ = new DataPackage(dpName, this);
    locations_ = new Locations(this);
    landmarksNew_ = new Landmarks(this);
    stations_ = new Stations(this);
    routes_ = new Routes(this);
    pathfinder_ = new Pathfinder(this);

    initTE_ = false;
  }

  private class DummyOutputAcceptor implements GUIOutputAcceptor {

    @Override
    public void msg(String text) {
    }

    @Override
    public void statusText(String text) {
    }

    @Override
    public void setProgress(int value) {
    }
  }

  /*public void clearNetwork() {
    baseNet_ = new BaseNetwork();
    stations_ = new Stations(this);
    routes_ = new Routes(this);
  }*/

  public void msg(String msg) {
    System.out.println("m: " + msg);
    if (guiOA_ != null) {
      guiOA_.msg(msg);
    }
  }

  public Properties getProperties() {
    return sysProps_;
  }

  public String getProperty(String propName) {
    return sysProps_.getProperty(propName);
  }
  
  public void setProperty(String propName, String value) {
    sysProps_.setProperty(propName, value);
  }

  //public Graph getGraph() { return baseNet_.getGraph(); }
  public DataPackage getDataPackage() {
    return dataPkg_;
  }

  public BaseNetwork getBaseNet() {
    return baseNet_;
  }

  public LegacyDB getDB() {
    return dataPkg_.getLegacyDB();
  }

  public Pathfinder getPathfinder() {
    return pathfinder_;
  }

  public Locations getLocations() {
    return locations_;
  }

  public Landmarks getLandmarksNew() {
    return landmarksNew_;
  }

  public Routes getRoutes() {
    return routes_;
  }

  public Scenarios getScenarios() {
    return dataPkg_.getScenarios();
  }

  public Stations getStations() {
    return stations_;
  }

  public RouteNetwork getRouteNetwork() {
    return rteNet_;
  }

  public void writeNetworkCacheFile() {
    String fs = File.separator;
    // todo: write scenarios?

    String filename = getProperty("5pHome") + "data" + fs + getDataPackage().getName() + fs + "network.fpn";
    baseNet_.writeToFile(filename);
    //routes_.writeToCacheFile(routesFile, guiOA_);
  }

  public void readNetworkCacheFile() {
    String fs = File.separator;
    String filename = getProperty("5pHome") + "data" + fs + getDataPackage().getName() + fs + "network.fpn";
    baseNet_.readFromFile(filename, this);
  }

  public void writeTransitCacheFile() {
    String fs = File.separator;
    // todo: write scenarios

    String routesFile = getProperty("5pHome") + "data" + fs + getDataPackage().getName() + fs + "routes.fpc";
    routes_.writeToCacheFile(routesFile, guiOA_);
  }

  public void readTransitCacheFile() {
    String fs = File.separator;

    // todo: read scenarios

    String routesFile = getProperty("5pHome") + "data" + fs + getDataPackage().getName() + fs + "routes.fpc";
    routes_.readFromCacheFile(routesFile, guiOA_);
  }

  public GUIOutputAcceptor getGUIOutputAcceptor() {
    return guiOA_;
  }

  public void setGUIOutputAcceptor(GUIOutputAcceptor oa) {
    guiOA_ = oa;
  }

  public BLink getLinkFromID(Integer id) {
    return baseNet_.getLinkFromID(id);
  }

  public BNode getNodeFromID(Integer id) {
    return baseNet_.getNodeFromID(id);
  }

  public void initTransitAnnotations() {
    if (!initTE_) {
      clearTransitAnnotations();
      for(Route route : routes_.getCollection())
        for(SubRoute sub : route.getSubRoutes())
          sub.initTransitElements(this);
      initTE_ = true;
      msg("Initialized Transit Annotations");
    }
  }

  public void clearTransitAnnotations() {
    if (initTE_) {
      baseNet_.clearTransitAnnotations();
      initTE_ = false;
      msg("Cleared Transit Annotations");
    }
  }

  public void buildRouteNetwork() {
    if (!initTE_) {
      initTransitAnnotations();
    }
    rteNet_ = new RouteNetwork(this);
    rteNet_.build(routes_);
    msg("Built Route Network");
  }

  public void clearRouteNetwork() {
    if (rteNet_ == null) {
      return;
    }
    for(Route rte : routes_.getCollection())
      for(SubRoute sub : rte.getSubRoutes())
        sub.clearRouteLinks(rteNet_);
    rteNet_.clearPathSearchElements();
    rteNet_ = null;
    msg("Cleared Route Network");
  }

  /////////////////////// NETWORK /////////////////////////

  private Geometry loadedArea_;

  public void initArea(double x1, double y1, double x2, double y2) {

    GeometryFactory gf = new GeometryFactory();

    Coordinate coords[] = new Coordinate[5];
    coords[0] = new Coordinate(x1, y1);
    coords[1] = new Coordinate(x1, y2);
    coords[2] = new Coordinate(x2, y2);
    coords[3] = new Coordinate(x2, y1);
    coords[4] = new Coordinate(x1, y1);

    LinearRing shell = gf.createLinearRing(coords);
    Polygon rect = gf.createPolygon(shell, null);
    
    Geometry toLoad;
    if(loadedArea_ == null)
      loadedArea_ = toLoad = rect;
    else 
      toLoad = rect.difference(loadedArea_);

    List<Polygon> polygons =  PolygonExtracter.getPolygons(toLoad);
    for(Polygon p : polygons) {
      /*System.out.println("poly size = "+p.getNumPoints());
      for(Coordinate c : p.getCoordinates())
        System.out.println(" "+c);*/

      // decompose p and load each component rectangle
      Set<Envelope> rects = RectilinearPolygonDecomposer.decompose(p);
      //System.out.println("RPD result count = "+rects.size());
      for(Envelope e : rects) {
        initNodes(e.getMinX(), e.getMinY(), e.getMaxX(), e.getMaxY());
        initLinks(e.getMinX(), e.getMinY(), e.getMaxX(), e.getMaxY());
      }
    }

    /*if(loadedArea_ == null)
      loadedArea_ = rect;
    double minX = Math.min(x1, x2), maxX = Math.max(x1, x2);
    double minY = Math.min(y1, y2), maxY = Math.max(y1, y2);
    initNodes(minX, minY, maxX, maxY);
    initLinks(minX, minY, maxX, maxY);*/

    loadedArea_ = loadedArea_.union(rect);
  }



  public Collection<BNode> initNodes(double x1, double y1, double x2, double y2) {


    System.out.println("querying nodes..");
    //guiOA_.statusText("loading nodes..");
    Collection<BNode> nodes = dataPkg_.getSpatialDataConn().nodesInRange(x1, y1, x2, y2);
    //guiOA_.statusText("");
    System.out.println("nodes found: "+nodes.size());
    return nodes;
  }

  public Collection<BLink> initLinks(double x1, double y1, double x2, double y2) {
    System.out.println("querying links..");
    //guiOA_.statusText("loading links..");
    Collection<BLink> links = dataPkg_.getSpatialDataConn().linksInRange(x1, y1, x2, y2);
    //guiOA_.statusText("");
    System.out.println("links found: "+links.size());
    return links;
  }

  public void initNodes(Collection<Integer> ids) {
    Set<Integer> toLoad = new HashSet<Integer>();
    for(int nodeID : ids) {
      if(getNodeFromID(nodeID) == null) toLoad.add(nodeID);
    }

    System.out.println("querying "+toLoad.size()+" nodes in set..");
    Collection<BNode> nodes = dataPkg_.getSpatialDataConn().nodesInSet(toLoad);
    System.out.println("found "+nodes.size()+" links");
  }

  public void initLinks(Collection<Integer> ids) {
    /*Set<Integer> nodeIDs = new HashSet<Integer>();
    for(int linkID : ids) {
      BLink link = getLinkFromID(linkID);
      nodeIDs.add(link.getFNode().getID());
      nodeIDs.add(link.getTNode().getID());
    }
    Collection<BNode> nodes = dataPkg_.getDataConn().nodesInSet(nodeIDs);
    System.out.println("found "+nodes.size()+" nodes");*/
    if(dataPkg_.getProperty("preloadNetwork").equals("true")) return;

    Set<Integer> toLoad = new HashSet<Integer>();
    for(int linkID : ids) {
      if(getLinkFromID(linkID) == null) toLoad.add(linkID);

    }

    System.out.println("querying "+toLoad.size()+" links in set..");
    Collection<BLink> links = dataPkg_.getSpatialDataConn().linksInSet(toLoad);
    System.out.println("found "+links.size()+" links");
  }


  public void loadNodesFromDB(boolean preload) {
    ResultSet rs = preload ? getDB().getAllNodes() : getDB().getRteNetNodes();
    try {
      int i = 0;
      while (rs.next()) {
        i++;
        if (i % 10000 == 0) {
          System.out.println(i + " nodes read");
        }
        loadSingleNode(rs);
      }
      rs.close();
      System.out.println(i + " nodes read total");
      baseNet_.setMaxNodeID(getDB().getMaxID("nodes"));
    } catch (Throwable e) {
      e.printStackTrace();
    }

  }

  public void loadSingleNode(ResultSet rs) {
    try {
      /*int id = rs.getInt(1);
      Scenario scen = scenarios_.getBase();
      double x = rs.getDouble(2), y = rs.getDouble(3);*/

      int id = rs.getInt(1);
      Scenario scen = dataPkg_.getScenarios().get(rs.getInt(2));
      double x = rs.getDouble(3), y = rs.getDouble(4);

      BNode nodeElement = new BNode(id, scen, x, y);
      baseNet_.addNode(nodeElement);
    } catch (SQLException ex) {
      ex.printStackTrace();
    }

  }

  public void loadLinksFromDB(boolean preload) {
    ResultSet rs = preload ? getDB().getAllLinks() : getDB().getRteNetLinks();
    try {
      int i = 0;
      while (rs.next()) {
        i++;
        if (i % 10000 == 0) {
          System.out.println(i + " links read");
        }
        loadSingleLink(rs);
      }
      rs.close();
      System.out.println(i + " links read total");
      baseNet_.setMaxLinkID(getDB().getMaxID("links"));
    } catch (Throwable e) {
      e.printStackTrace();
    }
  }

  public void loadNetworkFromSet(Set<Integer> linkIDs) {
    try {
      //getDB().setAutoCommit(false);
      ResultSet rsl = getDB().getFromSet("links", linkIDs);
      Set<Integer> nodeIDs = new HashSet<Integer>();

      int i = 0;
      while (rsl.next()) {
        i++;
        if (i % 1000 == 0) {
          System.out.println(i + " links read");
        }
        nodeIDs.add(rsl.getInt(3)); // fNode
        nodeIDs.add(rsl.getInt(4)); // tNode
      }
      System.out.println(i + " links read total");

      // NODES
      ResultSet rsn = getDB().getFromSet("nodes", nodeIDs);
      while (rsn.next()) {
        loadSingleNode(rsn);
      }

      // LINKS -- 2nd pass
      i = 0;
      rsl = getDB().getFromSet("links", linkIDs);
      //rsl.first();
      while (rsl.next()) {
        i++;
        loadSingleLink(rsl);
      }
      rsl.close();
      System.out.println(i + " links read total");

    //getDB().setAutoCommit(true);
    } catch (Throwable e) {
      e.printStackTrace();
    }

  }

  public void loadSingleLink(ResultSet rs) {
    try {
      int id = rs.getInt(1);
      int scenID = rs.getInt(2);
      Scenario s = dataPkg_.getScenarios().get(scenID);
      int type = rs.getInt(3);
      int fNodeID = rs.getInt(4);
      int tNodeID = rs.getInt(5);
      String name = rs.getString(6);
      int lenFt = (int) rs.getDouble(7);

      /*if (id == 1000103) {
        System.out.println("loading link");
      }*/

      if (!baseNet_.nodeExists(fNodeID)) {
        ResultSet rsn = getDB().getFromID("nodes", fNodeID);
        if (rsn.next()) {
          loadSingleNode(rsn);
        } else {
          System.out.println("Error loading node from db w/ id=" + fNodeID);
        }
        rsn.close();
      }
      if (!baseNet_.nodeExists(tNodeID)) {
        ResultSet rsn = getDB().getFromID("nodes", tNodeID);
        if (rsn.next()) {
          loadSingleNode(rsn);
        } else {
          System.out.println("Error loading node from db w/ id=" + tNodeID);
        }
        rsn.close();
      }
      BNode fNode = baseNet_.getNodeFromID(fNodeID);
      BNode tNode = baseNet_.getNodeFromID(tNodeID);

      if (fNode == null || tNode == null) {
        System.out.println("could not load link w/ id=" + id);
        return;
      }
      double fx = fNode.getX(), fy = fNode.getY();

      //CNode tNode = (CNode)	nodes_.find(tNodeID).element();
      double tx = tNode.getX(), ty = tNode.getY();

      BLink linkElement = null;
      int linkType = (type > 100) ? type / 100 : type;
      //if(id % 100 ==0) System.out.println(""+linkType);
      switch (linkType / 10) {
        case LinkTypes.MAJOR_TFARE:
        case LinkTypes.MINOR_TFARE:
        case LinkTypes.OTHER_STREET:
        case LinkTypes.NONWALKABLE:
          linkElement = new BLinkStreet(s, id, linkType, fNode, tNode, name, lenFt);
          if (type > 100) {
            if (type % 10 > 0) ((BLinkStreet) linkElement).setBikePedProv(type % 10);
            if (type/10 % 10 > 0) ((BLinkStreet) linkElement).setTransitProv(type/10 % 10);
          }
          break;

        case LinkTypes.BIKE_PED:
          linkElement = new BLinkBikePed(s, id, fNode, tNode, name, lenFt);
          break;
        case LinkTypes.TRANSIT:
          if (linkType == LinkTypes.TR_METRO) {
            linkElement = new BLinkMetro(s, id, fNode, tNode, name, lenFt);
          }
          if (linkType == LinkTypes.TR_LRT) {
            linkElement = new BLinkLRT(s, id, fNode, tNode, name, lenFt);
          }
          break;
        case LinkTypes.RAIL:
          linkElement = new BLinkRR(s, id, fNode, tNode, name, lenFt);
          break;
      }
      if (linkElement != null) {
        baseNet_.addLink(linkElement);
      }
    } catch (SQLException ex) {
      ex.printStackTrace();
    }
  }

  public void loadAddrRangesFromDB() {
    ResultSet rs = getDB().getAllRows("addrranges");
    try {
      int i = 0;
      while (rs.next()) {
        i++;
        if (i % 10000 == 0) {
          System.out.println(i + " addr ranges read");
        }
        int id = rs.getInt(1);
        int fAddrL = rs.getInt(2);
        int fAddrR = rs.getInt(3);
        int tAddrL = rs.getInt(4);
        int tAddrR = rs.getInt(5);
        int zipL = rs.getInt(6);
        int zipR = rs.getInt(7);
        BLink link = getLinkFromID(new Integer(id));
        if (link != null && link.getClassType() == BLink.CLASS_STREET) {
          ((BLinkStreet) link).setAddressRange(fAddrL, fAddrR, tAddrL, tAddrR);
          ((BLinkStreet) link).setZips(zipL, zipR);
        } else if (link == null) {
          System.out.println("Error loading addr ranges for " + id);
          badAddrLinkIDs_.add(id);
        }
      }
      rs.close();
      System.out.println(i + " addr ranges read total");
    } catch (Throwable e) {
      e.printStackTrace();
    }
  }

  public Set<Integer> getBadAddrLinkIDs() {
    return badAddrLinkIDs_;
  }

  public void loadShapePointsFromDB() {
    ResultSet rs = getDB().getAllRows("shapepoints");
    try {
      int i = 0;
      while (rs.next()) {
        i++;
        if (i % 10000 == 0) {
          System.out.println(i + " shppt blobs read");
        }
        int id = rs.getInt(1);
        java.sql.Blob blob = rs.getBlob(2);
        ObjectInputStream objIS = new ObjectInputStream(blob.getBinaryStream());
        int d = 0;
        List<Point2D.Double> pts = new LinkedList<Point2D.Double>();
        while (objIS.available() > 0) {
          double x = objIS.readDouble();
          double y = objIS.readDouble();
          pts.add(new Point2D.Double(x, y));
          d++;
        }
        BLink link = getLinkFromID(new Integer(id));
        if (link != null) {
          link.setShapePoints(pts);
        } else {
          System.out.println("Error loading shapepoints for link " + id);
          badShpPtsLinkIDs_.add(id);
        }
        //System.out.println("d="+d);
        objIS.close();
      }
      rs.close();
      System.out.println(i + " shppt blobs read total");
    } catch (Throwable e) {
      e.printStackTrace();
    }
  }

  public Set<Integer> getBadShpPtsLinkIDs() {
    return badShpPtsLinkIDs_;
  }

  public void loadElevInfoFromDB() {
    ResultSet rs = getDB().getAllRows("elevation");
    try {
      int i = 0;
      while (rs.next()) {
        i++;
        if (i % 10000 == 0) {
          System.out.println(i + " elev entries read");
        }
        int id = rs.getInt(1);
        java.sql.Blob blob = rs.getBlob(2);
        ObjectInputStream objIS = new ObjectInputStream(blob.getBinaryStream());
        List<Integer> list = new ArrayList<Integer>();
        while (objIS.available() > 0) {
          list.add(objIS.readInt());
        }
        float upFW = rs.getFloat(3);
        float upBW = rs.getFloat(4);
        BLink link = getLinkFromID(new Integer(id));
        if (link != null) {
          link.setElevList(list);
          link.setUpSlopeFW(upFW);
          link.setUpSlopeBW(upBW);
        } else {
          System.out.println("Error loading elevation info for link " + id);
          badElevLinkIDs_.add(id);
        }
      }
      rs.close();
      System.out.println(i + " elev entries read total");
    } catch (Throwable e) {
      e.printStackTrace();
    }
  }

  public Set<Integer> getBadElevLinkIDs() {
    return badElevLinkIDs_;
  }

  public void loadScenLinkTypesFromDB() {
    ResultSet rs = getDB().getAllRows("scenlinktypes");
    try {
      int i = 0;
      while (rs.next()) {
        i++;
        if (i % 10000 == 0) {
          System.out.println(i + " scen link type overrides read");
        }
        short scenID = rs.getShort(1);
        int linkID = rs.getInt(2);
        int newType= rs.getInt(3);
        BLink link = getLinkFromID(new Integer(linkID));
        if (link != null) {
          baseNet_.setScenLinkOverride(link, scenID, newType);
        } else {
          System.out.println("Error loading scen link type override for link " + linkID);
          //badElevLinkIDs_.add(id);
        }
      }
      rs.close();
      System.out.println(i + " scen link type overrides read total");
    } catch (Throwable e) {
      e.printStackTrace();
    }
  }

/////////////////////// ADDRESS/LOCATION /////////////////////////
  public Iterator<SubRouteInfo> getOutSubRouteInfo(BNode node) {

    List<SubRouteInfo> sris = new LinkedList<SubRouteInfo>();
    Iterator<BLink> links = baseNet_.incidentLinks(node).iterator();
    while (links.hasNext()) {
      BLink link = links.next();
      BNode fNode = getNodeFromID(new Integer(link.getFNodeID()));
      BNode tNode = getNodeFromID(new Integer(link.getTNodeID()));

      Iterator<SubRouteInfo> srisFT = link.getSubRouteInfoFT();
      while (srisFT.hasNext()) {
        SubRouteInfo sri = srisFT.next();
        if (fNode == node && sri.getDLI() != -1) {
          sris.add(sri);
        }
      }
      Iterator<SubRouteInfo> srisTF = link.getSubRouteInfoTF();
      while (srisTF.hasNext()) {
        SubRouteInfo sri = srisTF.next();
        if (tNode == node && sri.getDLI() != -1) {
          sris.add(sri);
        }
      }
    }
    return sris.iterator();
  }

  public Iterator<SubRouteInfo> getInSubRouteInfo(BNode node) {
    List<SubRouteInfo> sris = new LinkedList<SubRouteInfo>();
    Iterator<BLink> links = baseNet_.incidentLinks(node).iterator();
    while (links.hasNext()) {
      BLink link = links.next();
      BNode fNode = getNodeFromID(new Integer(link.getFNodeID()));
      BNode tNode = getNodeFromID(new Integer(link.getTNodeID()));

      //if(fNode != node && tNode != node) msg("Big problem!");
      SubRouteInfo sub;
      Iterator<SubRouteInfo> srisFT = link.getSubRouteInfoFT();
      //System.out.println("*** "+link.getID()+" FT ***");
      if (tNode == node) {
        while (srisFT.hasNext()) {
          SubRouteInfo sri = srisFT.next();
          if (sri.getDLI() != -1) {
            sris.add(sri);
          }
        }
      }

      Iterator<SubRouteInfo> srisTF = link.getSubRouteInfoTF();
      //System.out.println("*** "+link.getID()+" TF ***");
      if (fNode == node) {
        while (srisTF.hasNext()) {
          SubRouteInfo sri = srisTF.next();
          if (sri.getDLI() != -1) {
            sris.add(sri);
          }
        }
      }
    }
    return sris.iterator();
  }

  ////////////////////// STREET ALIASES ///////////////////////
  /*public boolean checkStreetAlias(String alias) {
  return locations_.checkStreetAlias(alias);
  }
  public void addStreetAlias(String alias, String target) {
  locations_.addStreetAlias(alias, target);
  }
  public void removeStreetAlias(String alias) {
  locations_.removeStreetAlias(alias);
  }
  public Iterator<String> getAliasesForStreet(String street) {
  return locations_.getAliasesForStreet(street);
  }*/
  public void writeStreetAliasFile() {
    locations_.writeStreetAliasFile("c://demory//data//xml//" + dataPkg_.getName() + "//streetalias.xml", guiOA_);
    guiOA_.setProgress(0);
  }

  public void readStreetAliasFile(String filename) {
    locations_.readStreetAliasFile(filename, this);
  }

  public void readZipsFile(String filename) {
    locations_.readZipsFile(filename);
  }

  public void shutDown() {
    System.out.println("Shutting down engine");
    dataPkg_.close();
    if(getDB() != null) getDB().close();
  }
}
